package cn.git.agent.enhance;

import cn.git.agent.AbstractClassEnhancePluginDefine;
import cn.git.agent.interceptor.ConstructMethodInterceptPoint;
import cn.git.agent.interceptor.InstanceMethodInterceptPoint;
import cn.git.agent.interceptor.StaticMethodInterceptPoint;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;

/** 
 * @description: 所有插件都继承AbstractClassEnhancePluginDefine
 * 此类完成了enhanceInstance，以及 enhanceStaticClass方法，完成增强(transform指定method以及interceptor)
 * @program: bank-credit-sy
 * @author: lixuchun
 * @create: 2024-12-25
 */
public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePluginDefine {

    /**
     * 增强实例与构造方法
     * builder.method(
     * 	ElementMatchers.namedOneOf(INSPECT_EXECUTE_METHOD,
     * 			INSPECT_EXECUTE_QUERY,
     * 			INSPECT_EXECUTE_UPDATE,
     * 			INSPECT_EXECUTE_LARGE_UPDATE)
     * )
     * .intercept(MethodDelegation.to(interceptor));
     *
     * @param typeDescription
     * @param builder
     * @param classLoader
     * @param enhancedContext
     * @return
     */
    @Override
    protected DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,
                                                     DynamicType.Builder<?> builder,
                                                     ClassLoader classLoader,
                                                     EnhancedContext enhancedContext) {
        // 获取实例以及构造方法拦截点
        InstanceMethodInterceptPoint[] instanceMethodsInterceptPoints = this.getInstanceMethodsInterceptPoints();
        ConstructMethodInterceptPoint[] constructMethodInterceptPoints = this.getConstructMethodInterceptPoint();

        // 是否存在构造器拦截点
        boolean existsConstructorInterceptPoint = false;
        if (constructMethodInterceptPoints != null && constructMethodInterceptPoints.length > 0) {
            existsConstructorInterceptPoint = true;
        }

        // 是否存在实例拦截点
        boolean existsInstanceInterceptPoint = false;
        if (instanceMethodsInterceptPoints != null && instanceMethodsInterceptPoints.length > 0) {
            existsInstanceInterceptPoint = true;
        }

        // 如果不存在拦截点，则直接返回
        if (!existsConstructorInterceptPoint && !existsInstanceInterceptPoint) {
            return builder;
        }

        // 为字节码新增属性，三个参数 名称，类型，修饰符 private | volatile ,并且新增get set方法
        // 对于同一个typeDescription，只能定义一个属性，否则会报错,执行一次
        /**
         * isAssignableTo作用
         * 如果 typeDescription 描述的是 EnhancedInstance 本身，返回 true。
         * 如果 typeDescription 描述的是 EnhancedInstance 的子类或实现了 EnhancedInstance 接口的类，也返回 true。
         * 否则返回 false。
         */
        if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {
            // 没有被扩展过判定
            if (!enhancedContext.isObjectExtended()) {
                builder = builder.defineField(CONTEXT_ATTR_NAME,
                        Object.class,
                        Opcodes.ACC_PRIVATE | Opcodes.ACC_VOLATILE)
                        // 指定实现的接口，get set
                        .implement(EnhancedInstance.class)
                        .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));

                // 修改扩展状态为已扩展过
                enhancedContext.objectExtendedCompleted();
            }
        }
        // 拼装方法拦截点
        String typeName = typeDescription.getTypeName();

        // 开始增强实例方法
        if (existsInstanceInterceptPoint) {
            for (InstanceMethodInterceptPoint point : instanceMethodsInterceptPoints) {
                // 静态方法匹配器
                ElementMatcher<MethodDescription> methodsMatcher = point.getMethodsMatcher();
                // 拦截器名称
                String interceptorClassName = point.getMethodInterceptor();
                if (interceptorClassName == null || interceptorClassName.trim().isEmpty()) {
                    throw new RuntimeException("要增强的实例方法类[{" + typeName + "}]未指定拦截器");
                }

                builder = builder.method(ElementMatchers.not(ElementMatchers.isStatic())
                                .and(methodsMatcher))
                        .intercept(MethodDelegation.withDefaultConfiguration()
                                .to(new InstanceMethodsInterceptor(interceptorClassName, classLoader)));
            }
        }

        // 开始增强构造方法
        if (existsConstructorInterceptPoint) {
            for (ConstructMethodInterceptPoint constructPoint : constructMethodInterceptPoints) {
                // 静态方法匹配器
                ElementMatcher<MethodDescription> methodsMatcher = constructPoint.getConstructMethodsMatcher();
                // 拦截器名称
                String interceptorClassName = constructPoint.getMethodInterceptor();
                if (interceptorClassName == null || interceptorClassName.trim().isEmpty()) {
                    throw new RuntimeException("要增强的构造方法类[{" + typeName + "}]未指定拦截器");
                }

                // 创建builder信息，指定拦截器信息
                builder = builder.method(methodsMatcher)
                        .intercept(SuperMethodCall.INSTANCE.andThen(
                                MethodDelegation.withDefaultConfiguration()
                                        .to(new ConstructorMethodsInterceptor(interceptorClassName, classLoader))
                        ));
            }
        }

        return builder;
    }

    /**
     * 增强静态方法
     * builder.method(
     * 	ElementMatchers.namedOneOf(INSPECT_EXECUTE_METHOD,
     * 			INSPECT_EXECUTE_QUERY,
     * 			INSPECT_EXECUTE_UPDATE,
     * 			INSPECT_EXECUTE_LARGE_UPDATE)
     * )
     * .intercept(MethodDelegation.to(interceptor));
     * @param typeDescription
     * @param builder
     * @param classLoader
     * @return
     */
    @Override
    protected DynamicType.Builder<?> enhanceStaticClass(TypeDescription typeDescription,
                                                        DynamicType.Builder<?> builder,
                                                        ClassLoader classLoader) {

        // 获取我们的静态拦截点
        StaticMethodInterceptPoint[] staticMethodInterceptPoint = this.getStaticMethodInterceptPoint();
        if (staticMethodInterceptPoint == null || staticMethodInterceptPoint.length == 0) {
            return builder;
        }

        // 拼装方法拦截点
        String typeName = typeDescription.getTypeName();
        for (StaticMethodInterceptPoint point : staticMethodInterceptPoint) {
            // 静态方法匹配器
            ElementMatcher<MethodDescription> staticMethodsMatcher = point.getStaticMethodsMatcher();
            // 拦截器名称
            String interceptorClassName = point.getMethodInterceptor();
            if (interceptorClassName == null || interceptorClassName.trim().isEmpty()) {
                throw new RuntimeException("要增强的类[{" + typeName + "}]未指定拦截器");
            }

            builder = builder.method(ElementMatchers.isStatic()
                    .and(staticMethodsMatcher))
                    .intercept(MethodDelegation.withDefaultConfiguration()
                            .to(new StaticMethodsInterceptor(interceptorClassName, classLoader)));
        }
        return builder;
    }
}
